package lv.solcraft.replication.server.bdb;

import lv.solcraft.replication.server.dao.DataAccessException;

import org.apache.log4j.Logger;

import com.sleepycat.je.DeadlockException;
import com.sleepycat.je.Transaction;
import com.sleepycat.util.ExceptionUnwrapper;

public abstract class TxTemplate<RETURN> {

	private static final Logger logger = Logger.getLogger(TxTemplate.class);

	private final Database database;

	private final int maxRetries;

	public TxTemplate(Database database, int maxRetries) {
		super();
		this.database = database;
		this.maxRetries = maxRetries;
	}
	
	public TxTemplate(Database database) {
		this(database, 0);
	}

	public RETURN execute() {
		try {
			return executeWithRetries();
		} catch (Exception e) {
			throw new DataAccessException("Failed to execute in transaction", e);
		}
	}

	public RETURN executeWithRetries() throws Exception {
		if (!database.isActiveTx()) {
			for (int i = 0;; i += 1) {
				try {
					database.beginTx();
					RETURN value = executeInTx(database.activeTx());
					database.commitTx();
					return value;
				} catch (Throwable e) {
					e = ExceptionUnwrapper.unwrapAny(e);
					if (database.isActiveTx()) {
						try {
							database.rollbackTx();
						} catch (Throwable e2) {
							logger.warn("Rollback failed", e2);
							i = maxRetries + 1;
						}
					}
					if (i >= maxRetries || !(e instanceof DeadlockException)) {
						if (e instanceof Exception) {
							throw (Exception) e;
						} else {
							throw (Error) e;
						}
					} else {
						logger.info("Retrying after deadlock exception");
					}
				}
			}
		} else {
			try {
				RETURN value = executeInTx(database.activeTx());
				return value;
			} catch (Exception e) {
				throw ExceptionUnwrapper.unwrap(e);
			}
		}
	}

	abstract protected RETURN executeInTx(Transaction tx) throws Exception;
}
